﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using MvvmFoundation.Wpf;
using GroundStation.Models;
using System.Collections;
using System.ComponentModel;
using System.Net.Sockets;
using System.IO;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Threading;
using GroundStation.Models.Launcher;
using System.Windows;

namespace GroundStation.ViewModels
{
    //this class is used as the view model for stationconsole view. It does the communication with the towertop and grpredict

    //****This view model assumes that the towertop is up and running, and capable of accepting mulitple tcp clients. If the tower top is not up and running
    //this will not work. In the settings are IP addresses for which you can change in order to set up a tower top simulator for your needs.
    class StationConsoleViewModel : ViewModelBase
    {
        #region private
        private System.Diagnostics.Process _hamlibProcess;
        private TcpListener _responseListener;
        private TcpClient _towerTop;
        private TcpClient _tcpMessageSender = SocketSingleton.messageClient;
        private ICommand _relay1OnOffCommand, _relay2OnOffCommand, _relay3OnOffCommand, _relay4OnOffCommand, _relay5OnOffCommand;
        private ICommand _abortCommand, _safeShutdownCommand, _sendCommand, _autoTrackCommand;
        private bool _isDirty = false, _isAzimuthUpdated = false, _isAltitudeUpdated = false;
        private bool[] _isRelayUpdated = { false, false, false, false, false };
        private bool[] _isRelayOn = { false, false, false, false, false };
        private string _azimuthAngle = "0", _altitudeAngle = "0", _powerValue = "0", _satelliteElevation = "0", _satelliteAzimuth = "0";
        private Message _towertopMessage;
        private bool _autoTrackOn = false;
        #endregion
        
        //Returns from the towertop if any of its components were updated succesffuly
        public bool AzimuthSuccess = true, AltitudeSuccess = true, Relay1Success = true, Relay2Success = true, Relay3Success = true, Relay4Success = true, Relay5Success = true;
        
        //logs the sent message into the database, with the payload associated with it
        private int SendMessageToTower(Message towerTopMessage)
        {
            AddMessageToDatabase(towerTopMessage.PayLoad);

            return 0;
        }

        private void AddMessageToDatabase(int payLoad)
        {
            using (SqlConnection con = new SqlConnection(Properties.Settings.Default.SDTConnectionString))
            {
                con.Open();
                string sql = "Insert into TransactionRecord (Callsign, Time, PayLoad) values (@callSign, @time, @payLoad) ";
                using (SqlCommand command = new SqlCommand(sql, con))
                {
                    try
                    {
                        command.Parameters.Add("@callSign");
                        command.Parameters.Add("@time");
                        command.Parameters.Add("@payLoad");
                        command.Parameters["@callSign"].Value = User.CallSign;
                        command.Parameters["@time"].Value = DateTime.Now;
                        command.Parameters["@payLoad"].Value = payLoad;
                        command.ExecuteNonQuery();
                    }
                    catch (Exception e)
                    {

                    }
                }
            }
        }

        #region commands
        public ICommand SendCommand
        {
            get
            {
                if (_sendCommand == null)
                    _sendCommand = new RelayCommand(() =>
                    {
                        if (_isAltitudeUpdated)
                        {
                            _towertopMessage = new AltitudeMessage();
                            _towertopMessage.Status = 1;
                            string payload = _altitudeAngle ;
                            _towertopMessage.SetPayload(payload);
                            _towertopMessage.SendMessage();
                        }
                        if (_isAzimuthUpdated)
                        {
                            _towertopMessage = new AzimuthMessage();
                            _towertopMessage.Status = 1;
                            string payload = _azimuthAngle ;//+1 is for the setter in the message protocol
                            _towertopMessage.SetPayload(payload);
                            _towertopMessage.SendMessage();
                        }
                        for (int relayCount = 0; relayCount < 5; relayCount++)
                        {
                            if (_isRelayUpdated[relayCount])
                            {
                                _towertopMessage = new RelayMessage();
                                _towertopMessage.Status = 1;
                                string payload=(relayCount + 5).ToString();
                                if (Relays[relayCount])
                                    payload =  payload+"1";
                                else
                                    payload =  payload+"0";
                                _towertopMessage.SetPayload(payload);
                                _towertopMessage.SendMessage();
                            }
                        }
                        ResetIsUpdated();

                    }, () =>
                    {
                        if (_isDirty == false)
                            return false;
                        return true;
                    });

                return _sendCommand;
            }
        }

        private void ResetIsUpdated()
        {
            _isAltitudeUpdated = false;
            _isAzimuthUpdated = false;
            for (int i = 0; i < _isRelayUpdated.Count(); i++)
            {
                _isRelayUpdated[i] = false;
            }

            _isDirty = false;
        }

        public ICommand AbortCommand
        {
            get
            {
                if (_abortCommand == null)
                    _abortCommand = new RelayCommand(() =>
                    {


                    }, () =>
                    {

                        return false;
                    });

                return _abortCommand;
            }
        }

        public ICommand SafeShutdownCommand
        {
            get
            {
                if (_safeShutdownCommand == null)
                    _safeShutdownCommand = new RelayCommand(() =>
                    {
                        Message safeShutdownMessage = new SafeShutdownMessage();
                       
                        safeShutdownMessage.Status = 4;
                        safeShutdownMessage.SendMessage();

                    }, () =>
                    {

                        return false;
                    });

                return _safeShutdownCommand;
            }
        }

        public ICommand Relay1OnOffCommand
        {
            get
            {
                if (_relay1OnOffCommand == null)
                    _relay1OnOffCommand = new RelayCommand(() =>
                    {
                        _isDirty = true;
                        _isRelayUpdated[0] = true;
                        Relays[0] = !Relays[0];
                        OnPropertyChanged("Relays");

                    }, () =>
                    {

                        return true;
                    });

                return _relay1OnOffCommand;
            }
        }

        public ICommand Relay2OnOffCommand
        {
            get
            {
                if (_relay2OnOffCommand == null)
                    _relay2OnOffCommand = new RelayCommand(() =>
                    {
                        _isDirty = true;
                        _isRelayUpdated[1] = true;
                        Relays[1] = !Relays[1];
                        OnPropertyChanged("Relays");

                    }, () =>
                    {

                        return true;
                    });

                return _relay2OnOffCommand;
            }
        }

        public ICommand Relay3OnOffCommand
        {
            get
            {
                if (_relay3OnOffCommand == null)
                    _relay3OnOffCommand = new RelayCommand(() =>
                    {
                        _isDirty = true;
                        _isRelayUpdated[2] = true;
                        Relays[2] = !Relays[2];
                        OnPropertyChanged("Relays");
                    }, () =>
                    {

                        return true;
                    });

                return _relay3OnOffCommand;
            }
        }

        public ICommand Relay4OnOffCommand
        {
            get
            {
                if (_relay4OnOffCommand == null)
                    _relay4OnOffCommand = new RelayCommand(() =>
                    {
                        _isDirty = true;
                        _isRelayUpdated[3] = true;
                        Relays[3] = !Relays[3];
                        OnPropertyChanged("Relays");
                    }, () =>
                    {

                        return true;
                    });

                return _relay4OnOffCommand;
            }
        }

        public ICommand Relay5OnOffCommand
        {
            get
            {
                if (_relay5OnOffCommand == null)
                    _relay5OnOffCommand = new RelayCommand(() =>
                    {
                        _isDirty = true;
                        _isRelayUpdated[4] = true;
                        Relays[4] = !Relays[4];
                        OnPropertyChanged("Relays");
                    }, () =>
                    {

                        return true;
                    });

                return _relay5OnOffCommand;
            }
        }

        //public ICommand AutoTrackCommand
        //{
        //    get
        //    {
        //        if (_autoTrackCommand == null)
        //            _autoTrackCommand = new RelayCommand(() =>
        //            {
        //                if (AutoTrackOn)
        //                    AutoTrackOn = false;
        //                else
        //                    AutoTrackOn = true;

        //                OnPropertyChanged("AutoTrackOn");

        //                //69ingchipmunks  MIKE ADD YOUR FUCKING CODE HERE.
        //            }, () =>
        //            {

        //                return true;
        //            });

        //        return _autoTrackCommand;
        //    }
        //}

        #endregion
        
        public bool AutoTrackOn
        {
            get
            {
                return _autoTrackOn;
            }
            set
            {
                _autoTrackOn = value;
                OnPropertyChanged("AutoTrackOn");
            }
        }
        public double SatelliteAzimuth { get; set; }
        public string SatelliteAzimuthString
        {
            get
            {
                return _satelliteAzimuth;
            }
            set
            {
                _satelliteAzimuth = value;
                OnPropertyChanged("SatelliteAzimuthString");
                SatelliteAzimuth = Convert.ToDouble(_satelliteAzimuth);
                OnPropertyChanged("SatelliteAzimuth");
            }
        }

        public bool[] Relays
        {
            get
            {
                return _isRelayOn;
            }
            set
            {
                _isRelayOn = value;
                OnPropertyChanged("Relays");
            }
        }

        public double SatelliteElevation { get; set; }
        public string SatelliteElevationString
        {
            get
            {
                return _satelliteElevation;
            }
            set
            {
                _satelliteElevation = value;
                OnPropertyChanged("SatelliteElevationString");
                SatelliteElevation = Convert.ToDouble(_satelliteElevation);
                OnPropertyChanged("SatelliteElevation");
            }
        }

        public double PowerValue { get; set; }
        public string PowerValueString
        {
            get
            {
                return _powerValue;
            }
            set
            {
                _powerValue = value;
                OnPropertyChanged("PowerValueString");
                PowerValue = Convert.ToDouble(_azimuthAngle);
                OnPropertyChanged("PowerValue");
            }
        }
        public User User { get; set; }
        public double AzimuthAngle { get; set; }
        public string AzimuthAngleString
        {
            get
            {
                return _azimuthAngle;
            }
            set
            {
                _azimuthAngle = value;
                OnPropertyChanged("AzimuthAngleString");
                try
                {
                    _isDirty = true;
                    _isAzimuthUpdated = true;
                    AzimuthAngle = Convert.ToDouble(_azimuthAngle) * -1;
                    if (AzimuthAngle * -1 < 0 || AzimuthAngle * -1 > 360)
                    {
                        throw new Exception();
                    }
                    _azimuthAngle = (AzimuthAngle * -1).ToString();
                }
                catch (Exception e)
                {
                    _azimuthAngle = "";
                    AzimuthAngle = 0;
                }
                OnPropertyChanged("AzimuthAngle");
            }
        }

        public double AltitudeAngle { get; set; }
        public string AltitudeAngleString
        {
            get
            {
                return _altitudeAngle;
            }
            set
            {
                _altitudeAngle = value;
                OnPropertyChanged("AltitudeAngleString");
                try
                {
                    _isAltitudeUpdated = true;
                    _isDirty = true;

                    AltitudeAngle = Convert.ToDouble(_altitudeAngle) * -1;
                    if (AltitudeAngle * -1 < 0 || AltitudeAngle * -1 > 110)
                    {
                        throw new Exception();
                    }
                    _altitudeAngle = (AltitudeAngle * -1).ToString();
                }
                catch (Exception e)
                {
                    AltitudeAngle = 0;
                    _azimuthAngle = "";
                }
                OnPropertyChanged("AltitudeAngle");
            }
        }

        //constructor, this takes in a user so that the transactions are logged with the correct call sign
        public StationConsoleViewModel(User user)
        {
            User = user;
            AutoTrackOn = false;
            BackgroundWorker bgResponse = new BackgroundWorker();
            bgResponse.WorkerReportsProgress = true;
            bgResponse.DoWork += new DoWorkEventHandler(ResponseListeningThread);
            bgResponse.ProgressChanged += new ProgressChangedEventHandler(ResponseReceived);
            bgResponse.RunWorkerAsync();
            BackgroundWorker bgHamlib = new BackgroundWorker();
            bgHamlib.WorkerReportsProgress = false;
            bgHamlib.DoWork += new DoWorkEventHandler(HamlibListeningThread);

            bgHamlib.RunWorkerAsync();
        }

        //parses data received from gpredict. This is running very slow, might be something to look at
        void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null && e.Data.StartsWith("rotctl(d): P"))
            {
                String[] ParsedData = e.Data.Split('\'');
                if (ParsedData.Length > 1)
                {
                    if (ParsedData[1] != "")
                    {
                        SatelliteAzimuthString = ParsedData[1];
                        Console.Out.WriteLine(SatelliteAzimuthString);
                        if (AutoTrackOn)
                        {
                            AzimuthAngleString = SatelliteAzimuthString;
                            _towertopMessage = new AzimuthMessage();
                            _towertopMessage.SetPayload(_azimuthAngle);
                            _towertopMessage.SendMessage();
                        }
                    }
                    if (ParsedData[3] != "")
                    {
                        SatelliteElevationString = ParsedData[3];
                        if (AutoTrackOn)
                        {
                            AltitudeAngleString = SatelliteElevationString;
                            _towertopMessage = new AltitudeMessage();
                            _towertopMessage.SetPayload(_altitudeAngle);
                            _towertopMessage.SendMessage();
                        }
                    }
                }
            }
        }
        //starts the hamlib listener to communicate with the gpredict satellite
        private void HamlibListeningThread(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bg = sender as BackgroundWorker;
            _hamlibProcess = new System.Diagnostics.Process();
            _hamlibProcess.StartInfo.UseShellExecute = false;
            _hamlibProcess.StartInfo.FileName = "cmd.exe";
            _hamlibProcess.StartInfo.RedirectStandardError = true;
            _hamlibProcess.StartInfo.RedirectStandardInput = true;
            _hamlibProcess.StartInfo.RedirectStandardOutput = true;
            _hamlibProcess.StartInfo.ErrorDialog = true;
            _hamlibProcess.StartInfo.CreateNoWindow = true;
            _hamlibProcess.ErrorDataReceived += ConsoleDataReceived;
            _hamlibProcess.OutputDataReceived += ConsoleDataReceived;
            
            if (_hamlibProcess.Start())
            {
                _hamlibProcess.BeginOutputReadLine();
                _hamlibProcess.BeginErrorReadLine();
                _hamlibProcess.StandardInput.WriteLine("rotctld -m 1 -vvvvv -t 4533 -T 127.0.0.1 -C min_az=0 -C max_az=360 -C min_el=0 -C max_el=110");
                _hamlibProcess.WaitForExit();
            }
        }
        //close connections when exiting station console
        public void CloseTCPConnections()
        {
            
            _hamlibProcess.Kill();
            if (_towerTop != null)
                _towerTop.Close();
            _towerTop = null;
        }

        private void ResponseListeningThread(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bg = sender as BackgroundWorker;

            /*_responseListener= new TcpListener(1987);
            _responseListener.Start();
            _towerTop = _responseListener.AcceptTcpClient();
            StreamReader responseStream = new StreamReader(_towerTop.GetStream());
            String response;
            */
            StreamReader sr = new StreamReader(SocketSingleton.messageClient.GetStream());
            string response;

            while (true)
            {
                try
                {
                    //something changed
                    while ((response = sr.ReadLine()) != "")
                    {
                        bg.ReportProgress(0, response);
                    }
                }
                catch (Exception exception)
                {
                    MessageBox.Show("The Station Console's connection to the tower top has terminated. Please close the station console and try again later.");
                    break;
                }
            }
        }
        //data has been received from the towertop, adjust the UI accordingly
        private void ResponseReceived(object sender, ProgressChangedEventArgs e)
        {
            string response = Convert.ToString(e.UserState);
            if (response != "" || response != null)
            {
                string status = response.Substring(0, 1);
                string id = response.Substring(1, 1);
                string payload = response.Substring(2, 6);
                double current = 5.5;
                if (status == "1")
                {
                    PowerValue = current * Convert.ToDouble(payload);
                }
                else
                {
                    switch (id)
                    {
                        case "1":
                            AzimuthAngleString = payload.TrimEnd('0');
                            AzimuthSuccess = true;
                            MessageBox.Show("The tower top has succesfully adjusted the azimuth angle of the antenna!");
                            break;
                        case "2":
                            AltitudeAngleString = payload.TrimEnd('0');
                            AltitudeSuccess = true;
                            MessageBox.Show("The tower top has succesfully adjusted the altitude angle of the antenna!");
                            break;
                        case "5":
                            Relay1Success = true;
                            MessageBox.Show("The tower top has succesfully adjusted relay 1!");
                            break;
                        case "6":
                            Relay2Success = true;
                            MessageBox.Show("The tower top has succesfully adjusted relay 2!");
                            break;
                        case "7":
                            Relay3Success = true;
                            MessageBox.Show("The tower top has succesfully adjusted relay 3!");
                            break;
                        case "8":
                            Relay4Success = true;
                            MessageBox.Show("The tower top has succesfully adjusted relay 4!");
                            break;
                        case "9":
                            Relay5Success = true;
                            MessageBox.Show("The tower top has succesfully adjusted relay 5!");
                            break;
                        default:
                            Console.WriteLine("Error Parsing Response!");
                            break;
                    }
                }
            }
        }
    }
}
